package com.lby.redisdemo.controller;

import com.lby.redisdemo.config.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName SeckillController
 * @Description TODO
 * @Author louby0923
 * @Date 2023/3/30 14:38
 * @Version 1.0
 */

@RestController
@RequestMapping("/seckill")
@Slf4j
public class SeckillController {
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private RedisUtil redisUtil;
    //记录实际卖出的商品数量
    private AtomicInteger successNum = new AtomicInteger(0);

    @GetMapping(value = "/init")
    public String init() {
        // 初始化库存数量，模拟库存只要5个商品，写入到redis中
        redisUtil.set("stock", 5);
        successNum.set(0);

        log.info("===>>>库存初始化成功，库存数为" + 5);
        return "初始化库存成功";
    }

    @GetMapping(value = "/reduce")
    public String reduce() {
        int stock = (Integer) redisUtil.get("stock");
        log.info("===>>>当前数量" + stock);
        // 模拟只减少一个库存
        stock = stock - 1;
        if (stock < 0) {
            log.info("===>>>库存不足");
            return "库存不足";
        }
        // 将剩余数量回写到redis
        redisUtil.set("stock", stock);
        // 记录实际卖出的商品数量（线程安全每个请求都会记录）
        log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());
        return "减少库存成功";
    }

    /**
     *
     * @return
     */
    @GetMapping(value = "/reduce1")
    public String reduce1() {
        // 开启事务
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        // 监视key
        redisTemplate.watch("stock");
        Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
        stock = stock - 1;
        if (stock < 0) {
            log.info("===>>>库存不足");
            return "库存不足";
        }
        redisTemplate.opsForValue().set("stock", stock);
        List results = redisTemplate.exec();

        if (results != null && results.size() > 0) {
            log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());
            return "减少库存成功";
        }
        return "库存不足";
    }

    /**
     * SessionCallback可以确保操作者为同一个线程，高并发情况下必须防止争抢
     * todo 推荐使用该方式
     *
     * @return
     */

    @GetMapping(value = "/reduce2")
    public String reduce2() {
        // 开启事务
        List results = (List) redisTemplate.execute(new SessionCallback<List>() {
            @Override
            public List<Object> execute(RedisOperations operations) throws DataAccessException {
                // 监视key
                operations.watch("stock");
                Integer stock = (Integer) operations.opsForValue().get("stock");
                operations.multi();
                if (stock <= 0) {
                    log.info("===>>>库存不足");
                    return null;
                }
                operations.opsForValue().decrement("stock");
                return operations.exec();
            }
        });

        if (results != null && results.size() > 0) {
            log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());
            return "减少库存成功";
        }
        return "库存不足";
    }


}
